using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Ltf.Diffs;
using Ltf.MonitorLizard.Helpers;
using Microsoft.TeamFoundation.VersionControl.Client;
using Ltf.Remoting.TFAccess;
using Ltf.Remoting.RemoteEvents;
using Ltf.Remoting.TeamFoundation;
using System.IO;

namespace Ltf.MonitorLizard.Forms
{
	[FormType(FormTypes.TwoWayDiff)]

	public partial class TwoWayDiff : Form
	{
		public enum DiffSide
		{
			Left,
			Right
		}

		public TwoWayDiff()
		{
			InitializeComponent();
		}

		private Workspace workspaceLeft;
		private Workspace workspaceRight;
		private TFServerConnection connectionLeft;
		private TFServerConnection connectionRight;



		public TwoWayDiff(string[] pathList, LizardEventArgs args)
			:this ()
		{
			if (pathList.Length == 1)
			{
				cmbLeftVersion.Enabled = false;
				cmbRightVersion.Enabled = false;
				connectionLeft = ServerConnections.GetConnectionForPath(pathList[0]);
				Blame(pathList[0]);

			}
			else
			{
	//		TODO: test and investigate when the paths are server ($) paths - this MustUndeleteParentException be supplied with and connection.
	//			string wspath = (pathList[0].StartsWith("$")) ? pathList[0] : pathList[1];
				connectionLeft = ServerConnections.GetConnectionForPath(pathList[0]);
				connectionRight = ServerConnections.GetConnectionForPath(pathList[1]);
	//			if (!wspath.StartsWith("$"))
				string pathLeft = pathList[0];
				string pathRight = pathList[1];


				if (!pathLeft.StartsWith("$") && !pathRight.StartsWith("$"))
				{
					workspaceLeft = connectionLeft.GetWorkspace(pathLeft);
					workspaceRight = connectionLeft.GetWorkspace(pathRight);
				}
				else if (!pathLeft.StartsWith("$"))
				{
					connectionRight = connectionLeft;
					workspaceLeft = connectionLeft.GetWorkspace(pathLeft);
					workspaceRight = workspaceLeft;
				}
				else if (!pathRight.StartsWith("$"))
				{
					connectionLeft = connectionRight;
					workspaceRight = connectionRight.GetWorkspace(pathRight);
					workspaceLeft = workspaceRight;
				}
				else
				{
					// without a connection we're a bit stuffed as we can not resolve workspaces just from 
					// server paths (we don't know which server to use!)
				}
				historySelectorLeft = new HistorySelector(cmbLeftVersion.ComboBox, connectionLeft, workspaceLeft);
				historySelectorRight = new HistorySelector(cmbRightVersion.ComboBox, connectionRight, workspaceRight);

				if (pathList.Length == 2)
					CompareSideBySide(pathList[0], pathList[1]);
			}
		}

		public TwoWayDiff(string path1, string path2, Workspace workspace1, Workspace workspace2)
			: this()
		{
			//TODO: this no longer uses the supplied workspace - check this is okay - remove workspace from sig.
			if (path1.StartsWith("$"))
			{
				workspaceLeft = workspace1;
				connectionLeft = ServerConnections.GetConnectionForServerPath(path1, workspace1);
			}
			else
			{
				connectionLeft = ServerConnections.GetConnectionForPath(path1);
				workspaceLeft = connectionLeft.GetWorkspace(path1);
			}


			if (path2.StartsWith("$"))
			{
				workspaceRight = workspace2;
				connectionRight = ServerConnections.GetConnectionForServerPath(path2, workspace2);
			}
			else
			{
				connectionRight = ServerConnections.GetConnectionForPath(path2);
				workspaceRight = connectionLeft.GetWorkspace(path2);
			}
			historySelectorLeft = new HistorySelector(cmbLeftVersion.ComboBox, connectionLeft, workspaceLeft);
			historySelectorRight = new HistorySelector(cmbRightVersion.ComboBox,connectionRight, workspaceRight);

			if (string.IsNullOrEmpty(path2))
				Blame(path1);
			else
				CompareSideBySide(path1, path2);

		}


		public string LeftFile;
		public string RightFile;

		string LeftFSFileName;
		string RightFSFileName;

		bool ignoreRefresh = false;
		bool sideBySide = true;

		HistorySelector historySelectorLeft;
		HistorySelector historySelectorRight;
		
		private int contextLines=3;

		public void SetUp(string left, string right)
		{
			LeftFile = left;
			RightFile = right;
			 LeftFSFileName = LizardFileAccess.GetFilePath(connectionLeft, left, "left");
			 RightFSFileName = LizardFileAccess.GetFilePath(connectionLeft, right, "right");
			if (LeftFile.StartsWith("$"))
			{
				LizardFileAccess.LoadVersions(connectionLeft, cmbLeftVersion.ComboBox, LeftFile);
				cmbLeftVersion.Enabled = true;
			}
			else
				cmbLeftVersion.Enabled = false;
			if (RightFile.StartsWith("$"))
			{
				LizardFileAccess.LoadVersions(connectionRight, cmbRightVersion.ComboBox, RightFile);
				cmbRightVersion.Enabled = true;
			}
			else
				cmbRightVersion.Enabled = false;
			if (historySelectorLeft!=null)
				historySelectorLeft.Path = left;
			if (historySelectorRight != null)
				historySelectorRight.Path = right;
		}

		public void CompareSideBySide(string left, string right)
		{
			tsblblInserts.Visible = true;
			tsblblDeletes.Visible = true;
			tsblblChanges.Visible = true;
			ignoreRefresh = true;
			SetUp(left, right);
			twoWayDiff1.SetCaptions(left, right);

			twoWayDiff1.ShowDiffSideBySide(LeftFSFileName, RightFSFileName);
			tsblblInserts.Text = string.Format("Inserts:{0}", twoWayDiff1.InsertsCount);
			tsblblDeletes.Text = string.Format("Deletes:{0}", twoWayDiff1.DeletesCount);
			tsblblChanges.Text = string.Format("Changes:{0}", twoWayDiff1.ChangesCount);
			ignoreRefresh = false;
		}

		public void CompareInline(string left, string right)
		{
			tsblblInserts.Visible = true;
			tsblblDeletes.Visible = true;
			tsblblChanges.Visible = true;
			ignoreRefresh = true;
			SetUp(left, right);
			twoWayDiff1.SetCaptions(left + "  :  " + right, string.Empty);

			twoWayDiff1.ShowDiffInLine(LeftFSFileName, RightFSFileName, contextLines);
			tsblblInserts.Text = string.Format("Inserts:{0}", twoWayDiff1.InsertsCount);
			tsblblDeletes.Text = string.Format("Deletes:{0}", twoWayDiff1.DeletesCount);
			tsblblChanges.Text = string.Format("Changes:{0}", twoWayDiff1.ChangesCount);
			ignoreRefresh = false;
		}

		public void Blame(string blameFile)
		{
			tsblblInserts.Visible = false;
			tsblblDeletes.Visible = false;
			tsblblChanges.Visible = false;
			ignoreRefresh = true;
			SetUp(blameFile, blameFile);
			twoWayDiff1.SetCaptions(string.Empty, blameFile);

			twoWayDiff1.ShowBlame(blameFile);
			ignoreRefresh = false;
		}

		private void toolStripButton2_Click(object sender, EventArgs e)
		{
			GotoPreviouschange();
		}

		private void GotoNextChange()
		{
			twoWayDiff1.Next();
		}

		private void toolStripButton1_Click(object sender, EventArgs e)
		{
			GotoNextChange();
		}

		private void GotoPreviouschange()
		{
			twoWayDiff1.Previous();
		}

		private void tsbViewMode_Click(object sender, EventArgs e)
		{
			RefreshView();
		}

		private void RefreshView()
		{
			if (ignoreRefresh)
				return;
			if (sideBySide)
				CompareSideBySide(LeftFile, RightFile);
			else
				CompareInline(LeftFile, RightFile);
		}

		private void tsbUseLocalVersion_Click(object sender, EventArgs e)
		{
			if (tsbUseLocalVersion.Checked)
			{
				SetFilePickingMode(false);

			}
			else
			{
				SetFilePickingMode(true);

			}
		}

		private void SetFilePickingMode(bool allowAny)
		{
			tsbOpenLeft.Enabled=allowAny;
			tsbOpenRepoLeft.Enabled=allowAny;
			tsbOpenRepoRight.Enabled=allowAny;
		}

		private string ShowFileDialog(string filename)
		{
			OpenFileDialog ofd = new OpenFileDialog();
			if (!filename.StartsWith("$"))
				ofd.FileName = filename;
			DialogResult dr = ofd.ShowDialog();
			if (dr == DialogResult.OK)
				return ofd.FileName;
			else
				return filename;
		}

		private string ShowRepoDialog(string filename, DiffSide side)
		{
			if (!filename.StartsWith("$"))
			{
				filename= LizardFileAccess.GetWorkingBaseRepoPath(filename);
			}

			TFProjectBrowser tfpb = new TFProjectBrowser(filename, ItemType.Any, (side==DiffSide.Left)?workspaceLeft: workspaceRight);
			tfpb.Connection = (side==DiffSide.Left)?connectionLeft: connectionRight;
			DialogResult dr = tfpb.ShowDialog();
			if (dr == DialogResult.OK)
			{
				int v=0;
				int.TryParse(tfpb.ProductVersion, out  v);
				string path = LizardFileAccess.SetPathVersion(tfpb.ProjectPath, v);
				return path;

				// this is a bit ugly, but convinient now that we can select from different connections in the browser.
				if (side == DiffSide.Left)
					connectionLeft = tfpb.Connection;
				else
					connectionRight = tfpb.Connection;
			}
			else
				return filename;
		}

		private void tsbOpenLeft_Click(object sender, EventArgs e)
		{
			string filename=string.Empty;
			if (!LeftFile.StartsWith("$"))
				filename = LeftFile;
			LeftFile = ShowFileDialog(LeftFile);
			cmbLeftVersion.Enabled = false;
			RefreshView();
		}

		private void tsbOpenRight_Click(object sender, EventArgs e)
		{
			string filename = string.Empty;
			if (!RightFile.StartsWith("$"))
				filename = LeftFile;
			RightFile = ShowFileDialog(LeftFile);
			cmbRightVersion.Enabled = false;
			RefreshView();
		}

		private void tsbOpenRepoLeft_Click(object sender, EventArgs e)
		{
			string filename = string.Empty;
			if (LeftFile.StartsWith("$"))
				filename = LeftFile;
			LeftFile = ShowRepoDialog(LeftFile, DiffSide.Left);
			cmbLeftVersion.Enabled = true;
			LizardFileAccess.LoadVersions(connectionLeft, cmbLeftVersion.ComboBox, LeftFile);
			RefreshView();
		}

		private void tsbOpenRepoRight_Click(object sender, EventArgs e)
		{
			string filename = string.Empty;
			if (RightFile.StartsWith("$"))
				filename = RightFile;
			RightFile = ShowRepoDialog(RightFile, DiffSide.Right);
			cmbRightVersion.Enabled = true;
			LizardFileAccess.LoadVersions(connectionRight, cmbRightVersion.ComboBox, RightFile);
			RefreshView();
		}


		private void tstbContextLines_Leave(object sender, EventArgs e)
		{
			int old = contextLines;
			int cl;
			if (int.TryParse(tstbContextLines.Text,out cl))
				contextLines=cl;
			else if (tstbContextLines.Text==string.Empty)
				contextLines = -1;
			else
				tstbContextLines.Text=(contextLines==-1)?string.Empty:contextLines.ToString();

			if (old!=contextLines)
				RefreshView();
		}

		private void cmbLeftVersion_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (LizardFileAccess.IsActivelyLoading(cmbLeftVersion.ComboBox))
				return;

			int v = 0;
			int.TryParse(cmbLeftVersion.SelectedItem.ToString(), out v);
			LeftFile = LizardFileAccess.RevisionPath(LeftFile, v);
			RefreshView();
		}

		private void cmbRightVersion_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (LizardFileAccess.IsActivelyLoading(cmbRightVersion.ComboBox))
				return;
			int v = 0;
			int.TryParse(cmbRightVersion.SelectedItem.ToString(), out v);
			RightFile = LizardFileAccess.RevisionPath(RightFile, v);
			RefreshView();
		}

		private void tsbLeftBlame_Click(object sender, EventArgs e)
		{
			twoWayDiff1.SetCaptions(string.Empty, LeftFile);
			twoWayDiff1.ShowBlame(LeftFile);
		}

		private void tsbRightBlame_Click(object sender, EventArgs e)
		{
			twoWayDiff1.SetCaptions(string.Empty, RightFile);
			twoWayDiff1.ShowBlame(RightFile);
		}

		private void tsmShowXLines_Click(object sender, EventArgs e)
		{
			tsmShowAllLines.Checked = false;
			tsmShow3Lines.Checked = false;
			tsmShow5Lines.Checked = false;
			tsmShow7Lines.Checked = false;
			tsmShowCustomLines.Checked = false;

			ToolStripMenuItem tsmItem = (ToolStripMenuItem)sender;
			tsmItem.Checked = true;
			int cl;
			int.TryParse(tsmItem.Tag.ToString(),out cl);

			if (cl == -2)
				if (!int.TryParse(tstbContextLines.Text, out cl))
				{
					cl = 3;
					tstbContextLines.Text = "3";
				}

			if (cl != contextLines)
			{
				contextLines = cl;
				sideBySide = false;
				RefreshView();
			}

		}

		private void tsbViewSideBySide_Click(object sender, EventArgs e)
		{
			sideBySide = true;
			RefreshView();
		}

		private void tsbViewInline_ButtonClick(object sender, EventArgs e)
		{
			sideBySide = false;
			RefreshView();
		}

		string lastDialogFolder = string.Empty;
		private void tsbMergeDifferences_Click(object sender, EventArgs e)
		{
			string name;
			int v;
			LizardFileAccess.GetNameAndVersionFromPath(Path.GetFileName(LeftFile), out name, out v);

			OpenFileDialog ofd = new OpenFileDialog();
			ofd.Title = "Select Target File";
			if (lastDialogFolder != string.Empty)
			{
				ofd.FileName = Path.Combine(lastDialogFolder, name);
			}
			else
				ofd.FileName = name;
			
			DialogResult ofddr = ofd.ShowDialog();

			if (ofddr==DialogResult.Yes && File.Exists(ofd.FileName))
			{
				lastDialogFolder = (Path.GetDirectoryName(ofd.FileName));
				TFServerConnection connection = ServerConnections.GetConnectionForPath(ofd.FileName);
				Workspace workspace = connectionLeft.GetWorkspace(ofd.FileName);

				if (workspace != null)
				{
					string serverItem = workspace.TryGetServerItemForLocalItem(ofd.FileName);
					if (serverItem != null)
					{
						ExtendedItem[] extItems = workspace.GetExtendedItems(new ItemSpec[] { new ItemSpec(serverItem, RecursionType.None) }, DeletedState.Any, ItemType.File)[0];
						if (extItems.Length == 1)
						{
							ExtendedItem extItem = extItems[0];
							if (extItem.ChangeType == ChangeType.None)
							{
								DialogResult dr = MessageBox.Show("Do you wish to check out the target file before merging changes?", "Check Out Target",
									MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
								if (dr == DialogResult.Cancel)
									return;
								else if (dr == DialogResult.Yes)
								{
									Ltf.Remoting.Operations.WorkspaceOperators wso = new Ltf.Remoting.Operations.WorkspaceOperators(connection, workspace);
									wso.PendEdit(new string[] { ofd.FileName });
								}
							}
						}
					}
				}

				string path1 = string.Empty;
				string path2 = string.Empty;
				ThreeWayDiff twd = new ThreeWayDiff(LeftFSFileName, RightFSFileName, ofd.FileName);
				twd.ShowDialog();
			}
		}

		private void tsbSwap_Click(object sender, EventArgs e)
		{

			string tmpLeft = LeftFile;
			ComboBox.ObjectCollection tmpLeftCmbItems = cmbLeftVersion.Items;
			string tmpLeftCmbText = cmbLeftVersion.Text;
			bool tmpLeftEnabled = cmbLeftVersion.Enabled;
			LeftFile = RightFile;

			LeftFile = RightFile;
			cmbLeftVersion.Items.Clear();
			foreach (object o in cmbRightVersion.Items)
			{
				cmbLeftVersion.Items.Add(o);
			}
			cmbLeftVersion.Text = cmbRightVersion.Text;
			cmbLeftVersion.Enabled = cmbRightVersion.Enabled;

			RightFile = tmpLeft;
			cmbLeftVersion.Items.Clear();
			foreach (object o in tmpLeftCmbItems)
			{
				cmbRightVersion.Items.Add(o);
			}
			cmbRightVersion.Text = tmpLeftCmbText;
			cmbRightVersion.Enabled = tmpLeftEnabled;

			RefreshView();
		}




		private void tsbFind_Click(object sender, EventArgs e)
		{
			twoWayDiff1.FindNext(tsbTxtFind.Text);
		}

		private void tsbSearchPrev_Click(object sender, EventArgs e)
		{
			twoWayDiff1.FindPrevious(tsbTxtFind.Text);
		}

	}
}